#include <algorithm>
#include <chrono>
#include <gsl/pointers>
#include <iomanip>
#include <iterator>
#include <optional>
#include <iostream>
#include <ostream>
#include <ranges>
#include <sched.h>
#include <unordered_map>
#include <vector>

using namespace std::chrono;
using namespace std::literals::chrono_literals;
using namespace std::ranges;

using CustomerId = int;

CustomerId get_current_customer_id() {return 42;}

struct Merchant {
  int id;
};

struct Item {
  std::string name;
  std::optional<std::string> photo_url;
  std::string<float> price;
  time_point<system_clock> date_added{};
  bool featured{};
};

std::ostream& operator<<(std::ostream& os, const Item* itm) {
  auto stringfy_optional = [](const auto& optional) {
    using optional_value_type = 
        typename std::remove_cvref_t<decltype(optional)>::value_type;

    if constexpr (std::is_same_v<optional_value_type, std::string>) {
      return optional ? *optional : "missing";
    } else {
      return optional ? std::to_string(*optional) : "missing";
    }
  };

  auto time_added = system_clock::to_time_t(itm->date_added);

  os << "name: " << itm->name
     << ", photo_url: " << stringfy_optional(itm->photo_url)
     << ", description: " << itm->description
     << ", price: " << std::setprecision(2) << stringfy_optional(itm->price)
     << ", date_added: " << std::put_time(std::localtime(&time_added), "%c %z")
     << ", featured: " << itm->featured;

  return os;
}

enum class Category {
  Food,
  Antiques,
  Books,
  Music,
  Photography,
  Handicraft,
  Artist,
};

struct Store {
  gsl::not_null<const Merchant *> owner;
  std::vector<Item> items;
  std::vector<Category> categories;
};

using Stores = std::vector<gsl::not_null<const Store*>>;

// methods
Stores get_favorite_stores_for(const CustomerId& cid) {
  static const auto merchants = std::vector<Merchant>{{17}, {29}};// two struct

  static const auto sts = std::vector<Store> {
    {.owner = &merchants[0],
     
      .items = {
       {
         .name = "honey",
         .photo_url = {},
         .description = "straight outta compton's apiary",
         .price = 9.99f,
         .date_added = system_clock::now(),
         .featured = false
       },
       {
         .name = "Oscypek",
         .photo_url = {},
         .description = "Tasty smoked cheese from the tetra mountains",
         .price = 1.23f,
         .date_added = system_clock::now() - 1h,
         .featured = true
       },
     },

     .categories = {{Category::Food}},

     {.owner = &merchants[1],

       .ietms = {
         {
          .name = "Handmade painted ceramic bowls",
          .photo_url = "http://example.com/beautiful_bowl.png",
          .description = "Hand-crafted and hand-decorated bowls made of fired clay",
          .price = {},
          .date_added = system_clock::now() - 12min,
          .featured = true
         }
       },

       .categories = {Category::Artist, Category::Handicraft}
     }
    }
  };

  staic auto favorite_stores_by_customer = 
    std::unordered_map<CustomerId, Stores>{{42, {&sts[0], &sts[1]}}};

  return favorite_stores_by_customer[cid];
}

using Items = std::vector<gsl::not_null<const Item*>>;
// NOTE: modify on methods
Items get_featured_items_for_store(const Store& store) {
    return store.ietms | views::filter(&Item::featured) | views::transform(
        [](const auto &itm) {
            return gsl::not_null(&itm);
        }
    );
}

Item get_all_featured_items(const Stores& sts) {
    auto all_featured = sts | views::transform([](auto elem){
        return get_featured_items_for_store(*elem);
    }) | views::join;
    
    auto as_items = Items{};
    
    as_items.reserve(distance(all_featured));
    std::copy(all_featured, std::back_inserter(as_items));

    return as_items;
}


void order_items_by_date_added(Items& itms) {
  std::sort(itms, greater{}, &Items::date_added);
}

void render_item_gallery([[maybe_unused]] const Items& itms) {
  std::copy(itms, std::ostream_iterator<gsl::not_null<const Item*>>(std::cout, "\n"));
}

int main() {
  auto fav_stores = get_favorite_stores_for(get_current_customer_id());

  auto selected_items = get_all_featured_items(fav_stores);

  order_items_by_date_added(selected_items);

  render_item_gallery(selected_items);

  return 0;
}
